/* SPDX-License-Identifier: GPL-2.0 */
/*
 * Copyright @ Huawei Technologies Co., Ltd. 2022-2023. ALL rights reserved.
 * Description: Header file for hpmm persistent memory.
 */

#ifndef _LINUX_HPMM_MGR_H
#define _LINUX_HPMM_MGR_H

#include <linux/mm.h>
#include <linux/hpmm.h>
#include <linux/types.h>
#include <linux/mm_types.h>
#include <linux/rbtree.h>
#include <asm/types.h>

#include "hpmm_persist.h"

enum mgr_type {
	HPMM_MGR_PGTABLE = 0x01,
	HPMM_MGR_UCE_RECORD,
};

enum mgr_info {
	HPMM_PGTABLE_HEAD = 0x00,
	HPMM_UCE_RECORD_HEAD,
};

/* The magic num used in data recovery verify */
#define HPMM_MGR_MAGIC			0xF5F1
#define HPMM_PGTABLE_HEAD_MAGIC		0xF6F2
#define HPMM_UCE_RECORD_HEAD_MAGIC	0xF7F3
/* hpmm_mgr_info max num  */
#define MGR_INFO_NUM			3
/* The max uce record */
#define MAX_UCE_RECORD_NR		128

struct pgtable_node {
	/* pte corresponding to the vma in user space */
	unsigned long addr;
	/* the process unique ID */
	unsigned long uuid;
	/* Position index in the bitmap */
	unsigned long index;
};

struct pgtable_head {
	/* the magic correspond to the pgtable_head, used in the create process */
	unsigned long magic;
	/* the max num of pgtable_node */
	unsigned long max_index;
	/* last used index in bitmap */
	unsigned long last;
	/* linear mapping start phy pfn */
	unsigned long linear_mapping_pfn;
	/* bbu memory used for linear mapping */
	unsigned long linear_mapping_nrpage;
	/* the pgtable_node start addr */
	void *page_base;
	/* used for pgtable node memory short */
	struct pgtable_head *next;
	/* new elem can be added up, not below */
	unsigned long *bitmap;
	/* the pgtable node bitmap addr */
	struct pgtable_node *node;
};

struct uce_record_head {
	/* the magic correspond to the uce_record_head */
	unsigned long magic;
	/* the max num of uce record */
	unsigned long max_index;
	/* last used index in bitmap */
	unsigned long last;
	/* records start addr */
	struct uce_record *records;
	/* bitmap start addr */
	unsigned long *bitmap;
} __aligned(8);

struct hpmm_mgr_context {
	/* checksum of this mgr */
	u16 mgr_sum;
	/* hpmem_mgr_context magic signature and other flag */
	u16 magic;
	unsigned long seq;
	unsigned int version;
	unsigned int flags;
	/* mgr type num */
	unsigned int num;
	struct {
		union {
			struct uce_record_head *uce_record_head;
			struct pgtable_head *pgtable_head;
		};
		unsigned long offset;
		unsigned long size;
		unsigned int type;
	} hpmm_mgr_info[0];
} __aligned(PAGE_SIZE);

#define for_each_pgtable(head, pgtable, idx)	\
	for (idx = 0, pgtable = head->node; idx < head->max_index; pgtable++, idx++)
#define for_each_uce_record(head, record, idx)	\
	for (idx = 0, record = head->records; idx < head->max_index; record++, idx++)

static inline unsigned long find_and_set_bit(unsigned long *bitmap,
					     unsigned long cur, unsigned long max)
{
	unsigned long index;
retry:
	index = find_next_zero_bit(bitmap, max, cur);
	if (index == max)
		index = find_first_zero_bit(bitmap, max);
	if (index == max)
		return -1;
	if (unlikely(test_and_set_bit(index, bitmap)))
		goto retry;
	return index;
}

int persist_memory_clear(struct hpmm_persist_device *dev, int flags, unsigned long uuid);
int persistent_mgr_init(void *start_addr, unsigned long size);
void reconstruct_persistent_mgr(unsigned long addr);
bool hpmm_is_free_buddy_page(struct page *page);
bool check_hpmm_mgr_context_valid(unsigned long addr);
#endif /* _LINUX_HPMM_MGR_H */

